package cn.tedu.bece.admin.common.aspect;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import cn.tedu.bece.admin.common.annotation.Log;
import cn.tedu.bece.admin.common.response.JsonResult;
import cn.tedu.bece.admin.common.util.SecurityUtils;
import cn.tedu.bece.admin.system.pojo.entity.SysOperateLog;
import cn.tedu.bece.admin.system.service.ISysOperateLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Autowired
    private ISysOperateLogService sysOperateLogService;

    @Pointcut("@annotation(cn.tedu.bece.admin.common.annotation.Log)")
    public void logPointcut() {
    }

    @Around("logPointcut()")
    public Object process(ProceedingJoinPoint jp) throws Throwable {
        Object result = null;
        long start = System.currentTimeMillis();
        try {
            result = jp.proceed();
        } catch (Throwable throwable) {
            result = JsonResult.error(throwable.getMessage());
            log.error("LogAspect error: {}", throwable.getMessage());
            throw throwable;
        } finally {
            long end = System.currentTimeMillis();
            SysOperateLog sysOperateLog = buildLog(jp, result, end - start);
            ThreadUtil.execute(() -> sysOperateLogService.insert(sysOperateLog));
        }
        return result;
    }

    private SysOperateLog buildLog(ProceedingJoinPoint joinPoint, Object result, long costTime) {
        // 获取目标方法
        Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        // 获取请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        // 获取用户名
        String username = null;
        try {
            username = SecurityUtils.getUsername();
        } catch (Exception ignored) {
        }
        // 获取ip
        String ip = ServletUtil.getClientIP(request);
        // 获取url
        String url = request.getRequestURI();
        // 获取请求方式
        String requestMethod = request.getMethod();
        // 获取操作方法
        String method = joinPoint.getTarget().getClass().getName() + "." + targetMethod.getName() + "()";
        // 获取参数
        Object[] args = joinPoint.getArgs();
        // 获取描述
        String description = null;
        if (targetMethod.isAnnotationPresent(Log.class)) {
            description = targetMethod.getAnnotation(Log.class).value();
        }
        // 封装日志
        SysOperateLog sysOperateLog = new SysOperateLog();
        sysOperateLog.setUsername(username);
        sysOperateLog.setIp(ip);
        sysOperateLog.setUrl(url);
        sysOperateLog.setRequestMethod(requestMethod);
        sysOperateLog.setMethod(method);
        sysOperateLog.setParam(JSONUtil.toJsonStr(args));
        sysOperateLog.setResult(JSONUtil.toJsonStr(result));
        sysOperateLog.setDescription(description);
        sysOperateLog.setCreateTime(new Date());
        sysOperateLog.setCostTime(costTime);
        // 返回日志对象
        return sysOperateLog;
    }

}
